package com.bootdo.api.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;

import org.apache.commons.lang3.StringUtils;


/**
 * 注解验证工具类
 * 
 * @author hrh
 * @date 2016年5月30日 上午8:56:51
 */
public class AnnotationValidateUtils {

	private static Validate validate;

	/**
	 * validate注解验证 (读取属性上注解)
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> boolean validateField(T t) throws Exception {
		// 获取object的类型
		Class<? extends Object> clazz = t.getClass();
		// 获取该类型声明的成员
		Field[] fields = clazz.getDeclaredFields();
		// 遍历属性
		for (Field field : fields) {
			// 对于private私有化的成员变量，通过setAccessible来修改器访问权限
			field.setAccessible(true);
			validate = field.getAnnotation(Validate.class);
			Object value = field.get(t);
			validate(field, field.getName(), value, validate);
			// 重新设置回私有权限
			field.setAccessible(false);
		}
		return true;
	}

	/**
	 * validate注解验证 (读取get方法上注解)
	 * 
	 * @param t
	 * @return
	 * @throws Exception
	 */
	public static <T> boolean validateMethod(T t) throws Exception {
		// 获取该类型声明的方法
		LinkedHashMap<Method, Validate> linkedHashMap = getMethodAndAnnotations(t);
		Iterator<Entry<Method, Validate>> it = linkedHashMap.entrySet().iterator();
		while (it.hasNext()) {
			Entry<Method, Validate> entry = it.next();
			Method method = entry.getKey();
			Validate validate = entry.getValue();
			Object value = method.invoke(t);
			validate(null, method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4), value,
					validate);
		}

		return true;
	}

	/**
	 * 获取类中 get方法和对应的 Validate注解
	 * 
	 * @param t
	 * @return LinkedHashMap<Method, Validate>
	 * @throws Exception
	 */
	public static <T> LinkedHashMap<Method, Validate> getMethodAndAnnotations(T t) {
		LinkedHashMap<Method, Validate> linkedHashMap = new LinkedHashMap<Method, Validate>();
		Method[] methods = t.getClass().getMethods();
		Field[] fields = t.getClass().getDeclaredFields();
		for (Method method : methods) {
			String methodName = method.getName();// 获得方法名
			for (Field field : fields) {
				String fileName = field.getName();
				String str = "get" + fileName.substring(0, 1).toUpperCase() + fileName.substring(1);
				if (str.equals(methodName)) {
					Validate validate = method.getAnnotation(Validate.class);
					if (validate != null) {
						linkedHashMap.put(method, validate);
					}
				}
			}
		}
		return linkedHashMap;
	}

	/**
	 * 根据validate 属性 循环验证
	 * 
	 * @param field
	 * @param filedName
	 *            属性名
	 * @param value
	 *            属性值
	 * @param validate
	 *            验证对象
	 * @return
	 * @throws Exception
	 */
	private static <T> boolean validate(Field field, String filedName, Object value, Validate validate)
			throws Exception {
		String description;

		if (validate == null)
			return true;
		// 获取属性名
		filedName = field == null ? filedName : field.getName();
		description = validate.description().equals("") ? filedName : validate.description();

		/************* 注解解析工作开始 ******************/
		// 非空验证
		if (!validate.nullable()) {
			if (value == null || StringUtils.isBlank(value.toString())) {
				throw new ValidateException(Status.FAIL, ExceptionType.E0002, description + " Can not be empty ",
						field);
			}
		}

		// 最大长度验证
		if (value != null && value.toString().length() > validate.maxLength() && validate.maxLength() != 0) {
			throw new ValidateException(Status.FAIL, ExceptionType.E0002,
					description + " Length cannot be greater than " + validate.maxLength(), field);
		}

		// 最小长度验证
		if (value != null && value.toString().length() < validate.minLength() && validate.minLength() != 0) {
			throw new ValidateException(Status.FAIL, ExceptionType.E0002,
					description + " Length cannot be less than " + validate.minLength(), field);
		}

		// 已定义的格式验证
		if (value != null && validate.regexType() != RegexType.NONE) {
			switch (validate.regexType()) {
			case NONE:
				break;
			case SPECIALCHAR: // 不能包含特殊字符
				if (RegexUtils.hasSpecialChar(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002,
							description + " Cannot contain special characters ", field);
				}
				break;
			case CHINESE: // 不能含有中文字符
				if (RegexUtils.isChinese2(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002,
							description + " Can not contain Chinese characters ", field);
				}
				break;
			case EMAIL: // 邮箱格式不正确
				if (!RegexUtils.isEmail(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002,
							description + " Mailbox format is not correct ", field);
				}
				break;
			case IP: // IP格式不正确
				if (!RegexUtils.isIp(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002,
							description + " IP format is not correct ", field);
				}
				break;
			case NUMBER: // 判断是否正整数
				if (!RegexUtils.isNumber(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002, description + " Not number ", field);
				}
				break;
			case PHONENUMBER:// 手机号码格式不正确
				if (!RegexUtils.isPhoneNumber(value.toString())) {
					throw new ValidateException(Status.FAIL, ExceptionType.E0002,
							description + " Phone number format is not correct ", field);
				}
				break;
			default:
				break;
			}
		}

		// 自定义格式验证
		if (value != null && !validate.regexExpression().equals("")) {

			if (!value.toString().matches(validate.regexExpression())) {
				throw new ValidateException(Status.FAIL, ExceptionType.E0002,
						description + " The format is not correct ", field);
			}
		}
		
		//验证是否是枚举
		if(value!=null && StringUtils.isNoneBlank(value.toString()) && validate.checkEnum()!=Object.class){
			try{
				@SuppressWarnings("unchecked")
				Method method = validate.checkEnum().getMethod("valueOf",String.class);
				method.invoke(String.class,(String)value);
			}catch(Exception e){
				throw new ValidateException(Status.FAIL, ExceptionType.E0002,
						description + " parameter error ", field);
			}
		}	
		return true;
	}

}
